/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef CLIF_TESTING_TYPE_CASTER_H_
#define CLIF_TESTING_TYPE_CASTER_H_

#include <Python.h>

#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>

#include "clif/testing/value_holder.h"

namespace clif_testing {

inline int get_value_direct(const ValueHolder& vh) {
  return vh.value;
}

inline int get_value_optional(const std::optional<ValueHolder>& vh) {
  if (vh.has_value()) {
    return vh.value().value;
  } else{
    return 0;
  }
}

inline int get_value_variant(const std::variant<ValueHolder>& vh) {
  return std::get<0>(vh).value;
}

inline ValueHolder return_value(int v) {
  return ValueHolder(v);
}

inline std::vector<ValueHolder> return_value_list(
    const std::vector<int>& values) {
  ValueHolderList res{};
  for (int value : values) {
    res.push(value);
  }
  return res.get_values();
}

inline ValueHolderFromOnly return_value_pyobjfrom_only(int v) {
  return ValueHolderFromOnly(v);
}

inline int get_value_pyobjas_only(const ValueHolderAsOnly& vh) {
  return vh.value;
}

inline int get_value_pybind11_ignore(const ValueHolderPybind11Ignore& vh) {
  return vh.value;
}

inline int get_value_template(const ValueHolderTemplate<int>& vh) {
  return vh.value;
}

inline ValueHolderTemplate<std::string> make_value_holder_string(
    const std::string& v) {
  return ValueHolderTemplate<std::string>(v);
}

inline int get_value_template_one_param(
    const ValueHolderTemplateParameterPack<int>& vh) {
  return vh.value;
}

inline int get_value_template_two_params(
    const ValueHolderTemplateParameterPack<int, int>& vh) {
  return vh.value + 1;
}

inline std::vector<PyObject *> pyobject_round_trip(
    const std::vector<PyObject*> vec) {
  for (PyObject* e : vec) {
    Py_INCREF(e);
  }
  return vec;
}

inline PyObject* return_pyobject_throw_python_exception(int value) {
  if (value < 0) {
    PyErr_Format(PyExc_ValueError, "Value < 0");
    return nullptr;
  }
  return PyLong_FromLong(value);
}

inline int consume_unique_ptr(std::unique_ptr<ValueHolder> vh) {
  return vh->value;
}

inline ValueHolderAbstract* abstract_raw_ptr_round_trip(
    ValueHolderAbstract* vh) {
  return vh;
}

inline std::shared_ptr<ValueHolderAbstract> abstract_shared_ptr_round_trip(
    std::shared_ptr<ValueHolderAbstract> vh) {
  return std::make_shared<ValueHolderConcrete>(vh->value);
}

inline std::unique_ptr<ValueHolderAbstract> abstract_unique_ptr_round_trip(
    std::unique_ptr<ValueHolderAbstract> vh) {
  return std::make_unique<ValueHolderConcrete>(vh->value);
}

inline std::shared_ptr<ValueHolderAbstractNoPyObjAs> return_abstract_no_pyobjas(
    int value) {
  return std::make_shared<ValueHolderConcreteNoPyObjAs>(value);
}

inline int consume_only_optional_conversion(
    const ValueHolderOnlyOptionalConversion& vh) {
  return vh.value;
}

inline int consume_only_ptr_to_ptr_conversion(
    const ValueHolderOnlyPtrToPtrConversion& vh) {
  return vh.value;
}

inline int consume_only_shared_ptr_conversion(
    std::shared_ptr<ValueHolderOnlySharedPtrConversion> vh) {
  return vh->value;
}

inline int consume_multiple_conversions(
    const ValueHolderMultipleConversions& vh) {
  return vh.value;
}

inline int consume_ptr_in_clif_use_comment(
    const ValueHolderPtrInCLIFUseComment* vh) {
  return vh->value;
}

inline int consume_python_error_in_conversions(
    const PythonErrorInConversions& vh) {
  return vh.value;
}

inline PythonErrorInConversions return_python_error_in_conversions(int v) {
  return PythonErrorInConversions(v);
}

inline ValueHolderAsString return_value_holder_as_string() {
  return ValueHolderAsString("123");
}

}  // namespace clif_testing

#endif  // CLIF_TESTING_TYPE_CASTER_H_
